#include <cstring>
#include <cstdlib>
#include <sstream>

#include "ActionParser.h"

#include <windows.h>
#include <WinUser.h>		// need to make this cross compatible

// Constructors
ActionParser::ActionParser( char * inipath )
{

    char iniBuffer[1024];
	
	// Parse options from the INI
    std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "keys", "allowed", "", iniBuffer, 1024, inipath );
	this->allowedActions = new std::vector< std::string >();
	char * tok = std::strtok( iniBuffer, ";" );
	while( tok != NULL )
	{
	
		// Make the string
		std::string str( tok );
		
		// Convert the string to upper
		for( int i = 0; str[ i ] != '\0'; i++ )
			str[i] = std::toupper( str[i] );
		
		// Add the string to the allowed actions
		this->allowedActions->push_back( str );
		
		// Get the next token
		tok = std::strtok( NULL, ";" );
	
	}
	
	// Keys config
    std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "keys", "multi-allowed", "false", iniBuffer, 1024, inipath );
	this->multiAllowed = ( std::strcmp( iniBuffer, "true" ) == 0 );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "keys", "multiVotingOnly", "false", iniBuffer, 1024, inipath );
	this->multiVotingOnly = ( std::strcmp( iniBuffer, "true" ) == 0 );
	
    std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "keys", "maxActions", "0", iniBuffer, 1024, inipath );
	this->maxActions = std::atoi( iniBuffer );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "keys", "maxRepeats", "0", iniBuffer, 1024, inipath );
	this->maxRepeats = std::atoi( iniBuffer );
	
	
	// Environment config
    std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "environment", "windowName", "Snes9X v1.43 for Windows", iniBuffer, 1024, inipath );
	this->windowName = iniBuffer;
	
    std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "environment", "keyDelay", "50", iniBuffer, 1024, inipath );
	this->keyDelay = std::atoi( iniBuffer );
	
	
	// Voting config
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "voting", "votingEnabled", "false", iniBuffer, 1024, inipath );
	this->votingEnabled = ( std::strcmp( iniBuffer, "true" ) == 0 );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "voting", "votingLeftTerm", "anarchy", iniBuffer, 1024, inipath );
	this->votingLeftTerm = iniBuffer;
	for( int i = 0; this->votingLeftTerm[ i ] != '\0'; i++ )
		this->votingLeftTerm[i] = std::toupper( this->votingLeftTerm[i] );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "voting", "votingRightTerm", "democracy", iniBuffer, 1024, inipath );
	this->votingRightTerm = iniBuffer;
	for( int i = 0; this->votingRightTerm[ i ] != '\0'; i++ )
		this->votingRightTerm[i] = std::toupper( this->votingRightTerm[i] );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "voting", "votingBounds", "100", iniBuffer, 1024, inipath );
	this->voteBounds = std::atoi( iniBuffer );
	
	std::memset( iniBuffer, 0, 1024 );
    GetPrivateProfileString( "voting", "actionVoteThreshold", "10", iniBuffer, 1024, inipath );
	this->actionVoteThreshold = std::atoi( iniBuffer );
	
	this->voteCount = 0;
	this->voteMode = VOTE_LEFT;
	
	this->actions = new std::vector< Action >();

}

// Destructor
ActionParser::~ActionParser()
{

	this->actions->clear();
	delete this->actions;
	this->allowedActions->clear();
	delete this->allowedActions;
	
	this->actions = NULL;
	this->allowedActions = NULL;
	this->maxActions = 0;

}

// Getters
int ActionParser::getMaxActions() const
{

	return this->maxActions;

}

std::vector< std::string > * ActionParser::getAllowedActions() const
{

	return this->allowedActions;

}

int ActionParser::getNumActions() const
{

	return this->actions->size();

}

Action * ActionParser::getAction( int index ) const
{

	if( index < this->getNumActions() )
		return &( this->actions->at( index ) );
	else
		return NULL;

}

VoteMode ActionParser::getVoteMode()
{

	return this->voteMode;

}

// Setters
void ActionParser::setMaxActions( int maxActions )
{

	this->maxActions = maxActions;

}

void ActionParser::setAllowedActions( std::vector< std::string > * allowedActions )
{

	this->allowedActions = allowedActions;

}

void ActionParser::addAction( Action & action )
{

	// Check to make sure we are allowed to add the action
	if( canAddAction( action.getName() ) )
	{
	
		if( this->getNumActions() >= this->maxActions )
			this->actions->erase( this->actions->end() );
			
		this->actions->insert( this->actions->begin(), action );
		
	}

}

void ActionParser::addAction( const char * user, const char * message )
{

	std::string str( message );
	std::string userStr( user );

	// convert message to upper
	for( int i = 0; str[ i ] != '\0'; i++ )
		str[i] = std::toupper( str[i] );
		
		
	if( this->votingEnabled )
	{
	
		if( str.compare( this->votingLeftTerm ) == 0 )
		{
		
			this->voteCount = ( this->voteCount <= ( -1 * this->voteBounds ) ? ( -1 * this->voteBounds ) : this->voteCount - 1 );
			
			// Change mode if bounds reached
			if( this->voteCount <= ( -1 * this->voteBounds ) )
			{
			
				if( this->voteMode != VOTE_LEFT )
				{
				
					this->voteCount = 0;
					this->voteMode = VOTE_LEFT;
					
					// Clear the actions vector / vote map when changing mode
					this->actions->clear();
					this->voteActions.clear();
					
					std::cout << "vote mode 0" << std::endl;
					return;
				
				}
			
			}
				
			// Print the vote & vote info
			std::cout << user << " " << str << std::endl;
			std::cout << "vote count " << this->voteCount << std::endl;
			
			return;
		
		}
		else if( str.compare( this->votingRightTerm ) == 0 )
		{
		
			this->voteCount = ( this->voteCount >= this->voteBounds ? this->voteBounds : this->voteCount + 1 );
		
			// Change mode if bounds reached
			if( this->voteCount >= this->voteBounds )
			{
			
				if( this->voteMode != VOTE_RIGHT )
				{
				
					this->voteCount = 0;
					this->voteMode = VOTE_RIGHT;
					
					// Clear the actions vector / vote map when changing mode
					this->actions->clear();
					this->voteActions.clear();
					
					std::cout << "vote mode 1" << std::endl;
					return;
				
				}
			
			}
				
			// Print the vote & vote info
			std::cout << user << " " << str << std::endl;
			std::cout << "vote count " << this->voteCount << std::endl;
				
			return;
		
		}
	
	}
	
	if( this->voteMode == VOTE_LEFT )
	{
	
		if( this->multiAllowed && !( this->multiVotingOnly ) )
		{
		
			this->addMultiAction( str, userStr );
		
		}
		else
		{
		
			Action action( str, userStr, 1 );
			this->addAction( action );
		
		}
	
	}
	else if( this->voteMode == VOTE_RIGHT )
	{
	
		if( this->multiAllowed )
		{
		
			this->voteMultiAction( str, "voted action" );
		
		}
		else
		{
		
			this->voteAction( str, "voted action" );
		
		}
	
	}

}

void ActionParser::voteAction( std::string message, std::string user )
{

	// Check to make sure we are allowed to add the action
	if( canAddAction( message ) )
	{
		
		// If not in the map, add to the map with value of 1
		bool shouldCheck = false;
		if( this->voteActions.find( message ) == this->voteActions.end() )
		{
		
			if( this->voteActions.size() < ( size_t )this->maxActions )
			{
			
				this->voteActions[ message ] = 1;
				shouldCheck = true;
				std::cout << "vote action " << message << " " << this->voteActions[ message ] << std::endl;
				
			}
			
		}
		else
		{
		
			this->voteActions[ message ] += 1;		// else, increment map value by 1
			shouldCheck = true;
			std::cout << "vote action " << message << " " << this->voteActions[ message ] << std::endl;
			
		}
			
		if( shouldCheck && this->voteActions[ message ] >= this->actionVoteThreshold )
		{
		
			// perform the action and clear the map
			Action action( message, user, 1 );
			this->actions->push_back( action );
			this->performAction();
			
			// Clear the map for the next vote
			this->voteActions.clear();
			
			std::cout << "vote action performed" << std::endl;
		
		}
		
	}

}

std::vector< Action > * ActionParser::parseMultiAction( std::string message, std::string user )
{

	int numActions = 0;
	std::vector< Action > * actionsToAdd = new std::vector< Action >();
	std::stringstream digitStream;

	while( message != "" && numActions < this->maxActions && numActions < this->maxRepeats )
	{
	
		// Check if the action is in our allowed actions
		bool found = false;
		for( std::vector< std::string >::iterator it = this->allowedActions->begin(); it != this->allowedActions->end(); it++ )
		{
		
			// parse multiple actions in the same name (or action#, parse the # out)
			std::size_t index = message.find( *it );
			if( index != std::string::npos )
			{
			
				// we found the action
				if( index == 0 )
				{
				
					// Get the action string
					std::string actStr = message.substr( index, ( *it ).size() );
					
					// remove from message string
					message = message.substr( index + ( *it ).size() );
					
					// check to see if the next character is a digit
					while( message != "" && isdigit( message[0] ) )
					{
					
						digitStream << message[0];
						message = message.substr( 1 );
					
					}
					
					// queue actions to add to the vector
					int num = std::atoi( digitStream.str().c_str() );
					num = ( num <= 0 ? 1 : ( num >= this->maxRepeats ? this->maxRepeats : num ) );
					numActions += num;
					
					// Make sure to cap the amount of actions
					if( numActions <= this->maxActions && numActions <= this->maxRepeats )
					{
						
						Action action( actStr, user, num );
						actionsToAdd->push_back( action );
					
					}
					
					// clear the digit stream!
					digitStream.str( "" );
					digitStream.clear();
					
					found = true;
					break;
				
				}
			
			}
		
		}
		
		if( !found )
		{
		
			// If not found, then it is invalid
			return NULL;
		
		}
	
	}
	
	return actionsToAdd;

}

void ActionParser::voteMultiAction( std::string message, std::string user )
{

	// Do nothing if no allowed actions
	if( this->maxActions <= 0 )
		return;
		
	// Get the actions from the string
	std::vector< Action > * actionsToAdd = this->parseMultiAction( message, user );
	if( actionsToAdd == NULL )
		return;
	
	
	std::stringstream actionStream;
	std::string actionStr;
	for( std::vector< Action >::iterator it = actionsToAdd->begin(); it != actionsToAdd->end(); it++ )
		actionStream << (*it).getName() << (*it).getAmount();
	
	// Add the action string for vote
	actionStr = actionStream.str();
	
	// If not in the map, add to the map with value of 1
	bool shouldCheck = false;
	if( this->voteActions.find( actionStr ) == this->voteActions.end() )
	{
	
		if( this->voteActions.size() < ( size_t )this->maxActions )
		{
		
			this->voteActions[ actionStr ] = 1;
			shouldCheck = true;
			std::cout << "vote action " << actionStr << " " << this->voteActions[ actionStr ] << std::endl;
			
		}
		
	}
	else
	{
	
		this->voteActions[ actionStr ] += 1;		// else, increment map value by 1
		shouldCheck = true;
		std::cout << "vote action " << actionStr << " " << this->voteActions[ actionStr ] << std::endl;
		
	}
	
	if( shouldCheck && this->voteActions[ actionStr ] >= this->actionVoteThreshold )
	{
	
		// perform the actions and clear the map
		for( std::vector< Action >::iterator it = actionsToAdd->begin(); it != actionsToAdd->end(); it++ )
			this->addAction( *it );
		
		// Perform the actions
		performAllActions();
		
		// Clear the map for the next vote
		this->voteActions.clear();
		
		std::cout << "vote action performed" << std::endl;
	
	}

}

void ActionParser::addMultiAction( std::string message, std::string user )
{

	// Do nothing if no allowed actions
	if( this->maxActions <= 0 )
		return;

	// Get the actions from the string
	std::vector< Action > * actionsToAdd = this->parseMultiAction( message, user );
	if( actionsToAdd == NULL )
		return;
	
	// Actually add the actions
	for( std::vector< Action >::iterator it = actionsToAdd->begin(); it != actionsToAdd->end(); it++ )
		this->addAction( *it );
	
	performAllActions();

}

// Functionality
bool ActionParser::canAddAction( std::string name )
{

	// Check the amount of actions
	if( this->maxActions <= 0 )
		return false;
	
	
	// Check if the action is in our allowed actions
	for( std::vector< std::string >::iterator it = this->allowedActions->begin(); it != this->allowedActions->end(); it++ )
	{
	
		if( name.compare( *it ) == 0 )
			return true;
	
	}
		
	// If it was not found in the allowed actions, return false (can't add)
	return false;

}

int ActionParser::typeToKey( std::string name )
{

	if( name.compare( "UP" ) == 0 )
	{
	
		return VK_UP;
	
	}
	else if( name.compare( "LEFT" ) == 0 )
	{
	
		return VK_LEFT;
	
	}
	else if( name.compare( "DOWN" ) == 0 )
	{
	
		return VK_DOWN;
	
	}
	else if( name.compare( "RIGHT" ) == 0 )
	{
	
		return VK_RIGHT;
	
	}
	else if( name.compare( "SELECT" ) == 0 )
	{
	
		return VK_SPACE;
	
	}
	else if( name.compare( "START" ) == 0 )
	{
	
		return VK_RETURN;
	
	}
	else
	{
	
		// If we make it this far, we should have a valid name, so just return the character that created it
		return name[0];
	
	}

}

void ActionParser::printActions()
{

	std::cout << "Actions: " << std::endl;
	for( std::vector< Action >::iterator it = this->actions->begin(); it != this->actions->end(); it++ )
	{
	
		std::cout << *it << std::endl;
	
	}
	std::cout << std::endl;

}

// Actions can only exist on the action queue if they were valid to begin with
void ActionParser::performAction()
{

	// If we don't have an action, do nothing
	if( this->maxActions <= 0 || this->getNumActions() <= 0 )
		return;

	Action action = this->actions->back();
		
	int key = this->typeToKey( action.getName() );
	if( key == -1 )
		return;
	
	for( int i = 0; i < action.getAmount(); i++ )
		Utility::sendInput( this->windowName, key, this->keyDelay );
		
	// Print the action we performed
	std::cout << action << std::endl;
	
	// Remove the performed action
	this->actions->erase( this->actions->end() );

}

void ActionParser::performAllActions()
{

	while( this->getNumActions() > 0 )
	{
	
		this->performAction();
	
	}

}
